#ifndef NIGHT_LAMBDA_BIND
#define NIGHT_LAMBDA_BIND

#include "core.hpp"

namespace night { namespace lambda { namespace detail {

template< typename F >
struct Bind : lambda::Lambda< Bind<F> >
{
	F fn;

	template< typename T1=void, typename T2=void, typename T3=void >
	struct sig {
		typedef typename detail::call<F>::template sig<T1,T2,T3>::result_type result_type;
	};

	Bind ( F & src ) : fn (src) {}

	template< typename T1 >
	typename sig<T1>::result_type operator () ( T1 & x ) const { return detail::call<F>::apply (fn,x); }

	template< typename T1, typename T2 >
	typename sig<T1,T2>::result_type operator () ( T1 & x, T2 & y ) const { return detail::call<F>::apply (fn,x,y); }

	template< typename T1, typename T2, typename T3 >
	typename sig<T1,T2,T3>::result_type operator () ( T1 & x, T2 & y, T3 & z ) const { return detail::call<F>::apply (fn,x,y,z); }

};

template<typename F, typename T1>
detail::proxy< F, detail::tuple<T1 const> > bind ( F & fn, T1 const & x ) {
	return detail::proxy< F, detail::tuple<T1 const> > ( fn, detail::make_tuple (x) );
}

template<typename F, typename T1>
detail::proxy< F, detail::tuple<T1> > bind ( F & fn, T1 & x ) {
	return detail::proxy< F, detail::tuple<T1> > ( fn, detail::make_tuple (x) );
}

template<typename F, typename T1>
detail::proxy< F, detail::tuple<T1 const> > bind ( F const & fn, T1 const & x ) {
	return detail::proxy< F, detail::tuple<T1 const> > ( fn, detail::make_tuple (x) );
}

template<typename F, typename T1>
detail::proxy< F, detail::tuple<T1> > bind ( F const & fn, T1 & x ) {
	return detail::proxy< F, detail::tuple<T1> > ( fn, detail::make_tuple (x) );
}

template<typename F, typename T1>
detail::proxy< detail::Bind<F*>, detail::tuple<T1 const> > bind ( F * fn, T1 const & x ) {
	return detail::proxy< detail::Bind<F*>, detail::tuple<T1 const> > ( fn, detail::make_tuple (x) );
}

template<typename F, typename T1>
detail::proxy< detail::Bind<F*>, detail::tuple<T1> > bind ( F * fn, T1 & x ) {
	return detail::proxy< detail::Bind<F*>, detail::tuple<T1> > ( fn, detail::make_tuple (x) );
}

template<typename F, typename T1, typename T2>
detail::proxy< F, detail::tuple<T1 const,T2 const> > bind ( F & fn, T1 const & x, T2 const & y ) {
	return detail::proxy< F, detail::tuple<T1 const,T2 const> > ( fn, detail::make_tuple (x,y) );
}

template<typename F, typename T1, typename T2>
detail::proxy< F, detail::tuple<T1,T2 const> > bind ( F & fn, T1 & x, T2 const & y ) {
	return detail::proxy< F, detail::tuple<T1,T2 const> > ( fn, detail::make_tuple (x,y) );
}

template<typename F, typename T1, typename T2>
detail::proxy< F, detail::tuple<T1 const,T2 const> > bind ( F const & fn, T1 const & x, T2 const & y ) {
	return detail::proxy< F, detail::tuple<T1 const,T2 const> > ( fn, detail::make_tuple (x,y) );
}

template<typename F, typename T1, typename T2>
detail::proxy< F, detail::tuple<T1,T2 const> > bind ( F const & fn, T1 & x, T2 const & y ) {
	return detail::proxy< F, detail::tuple<T1,T2 const> > ( fn, detail::make_tuple (x,y) );
}

template<typename F, typename T1, typename T2>
detail::proxy< detail::Bind<F*>, detail::tuple<T1 const,T2 const> > bind ( F * fn, T1 const & x, T2 const & y ) {
	return detail::proxy< detail::Bind<F*>, detail::tuple<T1 const,T2 const> > ( fn, detail::make_tuple (x,y) );
}

template<typename F, typename T1, typename T2>
detail::proxy< detail::Bind<F*>, detail::tuple<T1,T2 const> > bind ( F * fn, T1 & x, T2 const & y ) {
	return detail::proxy< detail::Bind<F*>, detail::tuple<T1,T2 const> > ( fn, detail::make_tuple (x,y) );
}

template<typename F, typename T1, typename T2, typename T3>
detail::proxy< F, detail::tuple<T1 const,T2 const,T3 const> > bind ( F & fn, T1 const & x, T2 const & y, T3 const & z ) {
	return detail::proxy< F, detail::tuple<T1 const,T2 const,T3 const> > ( fn, detail::make_tuple (x,y,z) );
}

template<typename F, typename T1, typename T2, typename T3>
detail::proxy< F, detail::tuple<T1,T2 const,T3 const> > bind ( F & fn, T1 & x, T2 const & y, T3 const & z ) {
	return detail::proxy< F, detail::tuple<T1,T2 const,T3 const> > ( fn, detail::make_tuple (x,y,z) );
}

template<typename F, typename T1, typename T2, typename T3>
detail::proxy< F, detail::tuple<T1 const,T2 const,T3 const> > bind ( F const & fn, T1 const & x, T2 const & y, T3 const & z ) {
	return detail::proxy< F, detail::tuple<T1 const,T2 const,T3 const> > ( fn, detail::make_tuple (x,y,z) );
}

template<typename F, typename T1, typename T2, typename T3>
detail::proxy< F, detail::tuple<T1,T2 const,T3 const> > bind ( F const & fn, T1 & x, T2 const & y, T3 const & z ) {
	return detail::proxy< F, detail::tuple<T1,T2 const,T3 const> > ( fn, detail::make_tuple (x,y,z) );
}

template<typename F, typename T1, typename T2, typename T3>
detail::proxy< detail::Bind<F*>, detail::tuple<T1 const,T2 const,T3 const> > bind ( F * fn, T1 const & x, T2 const & y, T3 const & z ) {
	return detail::proxy< detail::Bind<F*>, detail::tuple<T1 const,T2 const,T3 const> > ( fn, detail::make_tuple (x,y,z) );
}

template<typename F, typename T1, typename T2, typename T3>
detail::proxy< detail::Bind<F*>, detail::tuple<T1,T2 const,T3 const> > bind ( F * fn, T1 & x, T2 const & y, T3 const & z ) {
	return detail::proxy< detail::Bind<F*>, detail::tuple<T1,T2 const,T3 const> > ( fn, detail::make_tuple (x,y,z) );
}

} } } // end namespace night

#endif //  NIGHT_LAMBDA_BIND
